import { Injectable } from '@nestjs/common';
import * as https from 'https';
import * as fs from 'fs';
import * as path from 'path';
import * as yaml from 'js-yaml';
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import { EventEmitterService } from './event-emitter.service';
import { Cron, CronExpression } from '@nestjs/schedule';
const baseUrl = 'http://localhost:3000';
// const baseUrl = 'https://test.lifeiapp.com/api';
// const baseUrl = 'http://localhost:8887/api';

/**
 * { 123: { currentIndex: 0, completeCount: 0, data: { 1: 'filepath1', 2: 'filepath2' } } }
 * chunkSize 现在存储在独立的文件中：${requestId}__size
 */
type ReadData = {
  chunkSize: number;
  currentIndex: number;
  completeCount: number;
  data: Map<number, string>; // 存储文件路径而不是 Buffer
};

// 全局共享的 readyData Map
const globalReadyData = new Map<string, ReadData>();
@Injectable()
export class ProxyService {
  constructor(private eventEmitter: EventEmitterService) {}

  private handlingRequests = [];

  // 分块大小配置（字节）
  private readonly CHUNK_SIZE = 1024 * 1024; // 1MB
  private readonly MAX_RESPONSE_SIZE = 100 * 1024 * 1024; // 100MB
  // 记录响应数据的分块大小。响应结束时，把分块大小发给代理服务器
  private readonly chunkInfo = new Map<string, number>();
  private readonly readyData = ProxyService.getReadyData();

  static getReadyData(): Map<string, ReadData> {
    return globalReadyData;
  }

  // 保存 chunkSize 到文件
  private saveChunkSizeToFile(
    uuid: string,
    requestId: string,
    chunkSize: number,
  ): void {
    try {
      const fileName = `${uuid}__${requestId}__size`;
      const filePath = path.join(process.cwd(), 'temp', fileName);

      // 确保 temp 目录存在
      const tempDir = path.join(process.cwd(), 'temp');
      if (!fs.existsSync(tempDir)) {
        fs.mkdirSync(tempDir, { recursive: true });
      }

      fs.writeFileSync(filePath, chunkSize.toString(), 'utf8');
      console.log('chunkSize 已保存到文件:', filePath, '值:', chunkSize);
    } catch (error) {
      console.error('保存 chunkSize 文件失败:', error);
    }
  }

  // 从文件读取 chunkSize
  private getChunkSizeFromFile(
    uuid: string,
    requestId: string,
  ): number | undefined {
    try {
      const fileName = `${uuid}__${requestId}__size`;
      const filePath = path.join(process.cwd(), 'temp', fileName);

      if (fs.existsSync(filePath)) {
        const data = fs.readFileSync(filePath, 'utf8');
        const chunkSize = parseInt(data, 10);
        console.log('从文件读取 chunkSize:', filePath, '值:', chunkSize);
        return chunkSize;
      }
    } catch (error) {
      console.error('读取 chunkSize 文件失败:', error);
    }

    return undefined;
  }

  // 删除 chunkSize 文件
  private deleteChunkSizeFile(uuid: string, requestId: string): void {
    try {
      const fileName = `${uuid}__${requestId}__size`;
      const filePath = path.join(process.cwd(), 'temp', fileName);

      if (fs.existsSync(filePath)) {
        fs.unlinkSync(filePath);
        console.log('已删除 chunkSize 文件:', filePath);
      }
    } catch (error) {
      console.error('删除 chunkSize 文件失败:', error);
    }
  }

  async forwardHttp({ url, method, data, headers, params, uuid, requestId }) {
    const config: AxiosRequestConfig = {
      method: method.toUpperCase(),
      url,
      headers: {
        ...headers,
      },
      data,
      params,
    };
    this.eventEmitter.addRequest(uuid, requestId, {
      createAt: new Date().getTime(),
      config,
    });

    try {
      const res = await this.eventEmitter.solveRequest(uuid, requestId);
      console.log('返回数据 0012');
      return res;
    } catch (error) {
      console.error('Error forwarding request:', error.message);
      this.eventEmitter.emit('request.error', {
        key: `${uuid}__${requestId}`,
        url,
        method,
        error: error.message,
      });
      throw error;
    }
  }

  async solveRequest(data: {
    end: boolean;
    uuid: string;
    requestId: string;
    chunkSize: number;
    chunkIndex: number;
    bufferData: any;
  }) {
    if (data.end) {
      console.log('接受数据块个数', data.chunkSize, data.end, data);
      this.saveChunkSizeToFile(data.uuid, data.requestId, data.chunkSize);
    } else {
      const { uuid, requestId, chunkSize, chunkIndex, bufferData } = data;
      if (!this.readyData.has(`${uuid}__${requestId}`)) {
        this.readyData.set(`${uuid}__${requestId}`, {
          chunkSize: chunkSize,
          currentIndex: 0,
          completeCount: 0,
          data: new Map<number, string>(), // 明确指定类型为 Map<number, string>
        });
      }
      const readyData = this.readyData.get(`${uuid}__${requestId}`);
      if (chunkIndex === undefined) {
        console.log('chunkIndex 0001: ', chunkIndex);
      }
      console.log('接受数据块: ', chunkIndex);

      // 将 Buffer 数据保存到文件
      const fileName = `${uuid}__${requestId}__${chunkIndex}`;
      const filePath = path.join(process.cwd(), 'temp', fileName);

      // 确保 temp 目录存在
      const tempDir = path.join(process.cwd(), 'temp');
      if (!fs.existsSync(tempDir)) {
        fs.mkdirSync(tempDir, { recursive: true });
      }

      // 根据数据类型进行适当的转换和保存
      let buffer: Buffer;
      if (typeof bufferData === 'string') {
        // 如果是字符串，假设是 base64 编码
        try {
          buffer = Buffer.from(bufferData, 'base64');
        } catch (error) {
          console.error('Invalid base64 string:', error);
          // 如果不是有效的 base64，直接转换为 Buffer
          buffer = Buffer.from(bufferData, 'utf8');
        }
      } else if (Buffer.isBuffer(bufferData)) {
        // 如果已经是 Buffer，直接使用
        buffer = bufferData;
      } else {
        // 其他类型，转换为字符串再转为 Buffer
        buffer = Buffer.from(String(bufferData), 'utf8');
      }

      // 保存到文件
      fs.writeFileSync(filePath, buffer);
      console.log('数据块已保存到文件:', filePath);

      // 存储文件路径而不是 Buffer
      readyData.data.set(chunkIndex, filePath);
    }
  }

  async getAllRequests(uuid: string) {
    const requests = this.eventEmitter.getAllRequests(uuid);
    Object.keys(requests).forEach((key) => {
      if (requests[key].handleTime) {
        if (new Date().getTime() - requests[key].handleTime > 1000 * 60) {
          delete requests[key];
        }
      } else {
        requests[key].handleTime = new Date().getTime();
      }
    });
    return requests;
  }

  // 发送数据到客户端
  sendDataByCycle(uuid: string, requestId: string, res) {
    let count = 0;
    const interval = setInterval(() => {
      const readyDataOfRequest: ReadData = this.readyData.get(
        `${uuid}__${requestId}`,
      );
      if (!readyDataOfRequest) {
        return;
      }
      const { currentIndex } = readyDataOfRequest;
      const chunkSize = this.getChunkSizeFromFile(uuid, requestId);

      if (currentIndex >= chunkSize && chunkSize > 0) {
        clearInterval(interval);
        res.end();
        console.log('res.end()');
        this.readyData.delete(`${uuid}__${requestId}`);
        // 清理所有相关的临时文件
        this.cleanupTempFiles(uuid, requestId);
        return;
      }
      const fileName = `${uuid}__${requestId}__${currentIndex}`;
      const filePath = path.join(process.cwd(), 'temp', fileName);
      if (filePath && fs.existsSync(filePath)) {
        try {
          const buffer = fs.readFileSync(filePath);
          res.write(buffer, () => {
            fs.unlinkSync(filePath);
          });
          readyDataOfRequest.currentIndex++;
        } catch (error) {
          console.error(error);
        }
      }

      count++;
      if (count > 6000) {
        clearInterval(interval);
        this.cleanupTempFiles(uuid, requestId);
      }
    }, 100);
  }

  // 清理指定 requestId 的所有临时文件
  private cleanupTempFiles(uuid: string, requestId: string) {
    const tempDir = path.join(process.cwd(), 'temp');
    if (!fs.existsSync(tempDir)) {
      return;
    }

    try {
      const files = fs.readdirSync(tempDir);
      files.forEach((file) => {
        if (file.startsWith(`${uuid}__${requestId}__`)) {
          const filePath = path.join(tempDir, file);
          try {
            fs.unlinkSync(filePath);
            console.log('清理临时文件:', filePath);
          } catch (error) {
            console.error('删除临时文件失败:', filePath, error);
          }
        }
      });
    } catch (error) {
      console.error('清理临时文件时出错:', error);
    }
  }

  @Cron(CronExpression.EVERY_10_SECONDS)
  async sendForSaleSchedule() {
    console.log('sendForSaleSchedule');
    let res;
    try {
      const uuid = this.getUuid();
      if (uuid) {
        res = await axios.get(`${baseUrl}/getAllRequests?uuid=${uuid}`);
      } else {
        res = await axios.get(`${baseUrl}/getAllRequests`);
      }
    } catch (error) {
      console.error('error 0009: ', error);
    }

    console.log('this.handlingRequests 0011: ', this.handlingRequests);
    if (res?.data) {
      if (res?.data?.data) {
        res.data = res.data.data;
      }
      const uuidAndReqeustIds = Object.keys(res.data);
      this.handlingRequests = this.handlingRequests.filter((uuidAndRequestId) =>
        uuidAndReqeustIds.includes(uuidAndRequestId),
      );
      await Promise.all(
        uuidAndReqeustIds.map(async (key: string) => {
          if (this.handlingRequests.includes(key)) {
            if (new Date().getTime() - res.data[key].handleTime > 1000 * 60) {
              this.handlingRequests = this.handlingRequests.filter(
                (uuidAndReqeustId) => uuidAndReqeustId !== key,
              );
            }
          } else {
            this.handlingRequests.push(key);
            const [uuid, requestId] = key.split('__');
            await this.transferRequest(uuid, requestId, res.data[key].config);
          }
        }),
      );
    }
  }

  /**
   * 分块处理响应数据
   */
  private async processResponseInChunks(
    uuid: string,
    requestId: string,
    response: AxiosResponse,
  ): Promise<any> {
    const contentType = response.headers['content-type'] || '';
    const contentLength = parseInt(response.headers['content-length'] || '0');

    // 检查响应数据类型
    const responseData = response.data;

    // 处理 IncomingMessage 类型（HTTP 响应流）
    if (
      responseData &&
      typeof responseData === 'object' &&
      responseData.readable !== undefined
    ) {
      console.log('Detected IncomingMessage stream, processing as stream');
      return await this.processStreamResponse(uuid, requestId, response);
    }

    // 如果是流式数据或大文件，使用流式处理
    if (
      contentLength > this.MAX_RESPONSE_SIZE ||
      contentType.includes('stream')
    ) {
      return await this.processStreamResponse(uuid, requestId, response);
    }

    // 处理 Buffer 类型数据
    return await this.processBufferData(responseData);
  }

  /**
   * 处理流式响应
   */
  private async processStreamResponse(
    uuid: string,
    requestId: string,
    response: AxiosResponse,
  ): Promise<any> {
    return new Promise((resolve, reject) => {
      const chunks: Buffer[] = [];
      let totalSize = 0;
      let chunkIndex = 0;

      console.log('Processing stream response...');
      console.log('Stream type:', response.data?.constructor?.name);
      console.log('Is readable:', response.data?.readable);

      // 处理 IncomingMessage 或其他可读流
      if (
        response.data &&
        typeof response.data === 'object' &&
        response.data.readable !== undefined
      ) {
        console.log('Processing as readable stream');

        response.data.on('data', (chunk: Buffer) => {
          console.log(
            'Received chunk, size:',
            chunk.length,
            'chunkIndex: ',
            chunkIndex,
          );
          totalSize += chunk.length;
          if (totalSize > this.MAX_RESPONSE_SIZE) {
            console.log('Response too large, destroying stream');
            response.data.destroy();
            reject(new Error('Response too large'));
            return;
          }
          this.sendChunk(uuid, requestId, chunk, chunkIndex, -1);
          this.readyData.get(`${uuid}__${requestId}`).completeCount =
            chunkIndex;
          chunkIndex++;
        });

        response.data.on('end', async () => {
          console.log('Stream ended, total size:', totalSize);
          const buffer = Buffer.concat(chunks);
          console.log('Final buffer size:', buffer.length);
          const readyData = this.readyData.get(`${uuid}__${requestId}`);
          readyData.chunkSize = chunkIndex;

          try {
            this.sendChunkSize(uuid, requestId);
          } catch (error) {
            console.error('error 0022: ', error);
          }
        });

        response.data.on('error', (error) => {
          console.error('Stream error:', error);
          reject(error);
        });

        // 处理流结束但没有数据的情况
        response.data.on('close', () => {
          console.log('Stream closed');
          if (chunks.length === 0) {
            resolve(null);
          }
        });
      } else {
        console.log('Not a readable stream, resolving directly');
        resolve(response.data);
      }
    });
  }

  /**
   * 处理 Buffer 数据
   */
  private async processBufferData(buffer: Buffer): Promise<any> {
    console.log(`Processing Buffer data, size: ${buffer.length} bytes`);

    // 如果 Buffer 较小，直接返回
    if (buffer.length <= this.CHUNK_SIZE) {
      return {
        type: 'buffer',
        size: buffer.length,
        data: buffer.toString('base64'), // 转换为 base64 字符串便于传输
        encoding: 'base64',
      };
    }

    // 如果 Buffer 较大，分块处理
    const chunks = [];
    for (let i = 0; i < buffer.length; i += this.CHUNK_SIZE) {
      const chunk = buffer.slice(i, i + this.CHUNK_SIZE);
      chunks.push(chunk);
    }

    console.log(`Processing large Buffer data in ${chunks.length} chunks`);

    // 将每个 chunk 转换为 base64
    const base64Chunks = chunks.map((chunk) => chunk.toString('base64'));

    return {
      type: 'large_buffer',
      chunkCount: chunks.length,
      totalSize: buffer.length,
      chunks: base64Chunks,
      encoding: 'base64',
    };
  }

  /**
   * 分块发送数据到目标服务器
   */
  private async sendDataInChunks(
    uuid: string,
    requestId: string,
    data: any,
  ): Promise<void> {
    // 处理 Buffer 类型数据
    if (data.type === 'buffer' || data.type === 'large_buffer') {
      await this.sendBufferData(uuid, requestId, data);
      return;
    }

    const dataString = JSON.stringify(data);

    if (dataString.length <= this.CHUNK_SIZE) {
      // 数据较小，直接发送
      await this.sendChunk(uuid, requestId, data, 0, 1);
      return;
    }

    // 数据较大，分块发送
    const chunks = [];
    for (let i = 0; i < dataString.length; i += this.CHUNK_SIZE) {
      chunks.push(dataString.slice(i, i + this.CHUNK_SIZE));
    }

    console.log(
      `Sending data in ${chunks.length} chunks for request ${requestId}`,
    );

    for (let i = 0; i < chunks.length; i++) {
      await this.sendChunk(uuid, requestId, chunks[i], i, chunks.length);
      // 添加小延迟避免服务器压力过大
      await new Promise((resolve) => setTimeout(resolve, 100));
    }
  }

  /**
   * 发送 Buffer 数据
   */
  private async sendBufferData(
    uuid: string,
    requestId: string,
    bufferData: any,
  ): Promise<void> {
    if (bufferData.type === 'buffer') {
      // 单个 Buffer，直接发送
      await this.sendChunk(uuid, requestId, bufferData.data, 0, 1);
    } else if (bufferData.type === 'large_buffer') {
      // 大型 Buffer，分块发送
      console.log(
        `Sending large Buffer data in ${bufferData.chunkCount} chunks for request ${requestId}`,
      );

      for (let i = 0; i < bufferData.chunks.length; i++) {
        const chunkData = {
          type: 'buffer_chunk',
          chunkIndex: i,
          totalChunks: bufferData.chunkCount,
          data: bufferData.chunks[i],
          encoding: bufferData.encoding,
          totalSize: bufferData.totalSize,
        };

        await this.sendChunk(
          uuid,
          requestId,
          chunkData,
          i,
          bufferData.chunkCount,
        );
        // 添加小延迟避免服务器压力过大
        await new Promise((resolve) => setTimeout(resolve, 100));
      }
    }
  }

  /**
   * 发送单个数据块到代理服务器
   */
  private async sendChunk(
    uuid: string,
    requestId: string,
    chunkData: any,
    chunkIndex: number,
    totalChunks: number,
  ): Promise<void> {
    try {
      if (chunkIndex === undefined) {
        console.log('chunkIndex 0002: ', chunkIndex);
      }

      // 根据数据类型选择合适的编码方式
      let encodedData;
      if (Buffer.isBuffer(chunkData)) {
        // 对于 Buffer 类型，使用 base64 编码保持二进制数据完整性
        encodedData = chunkData.toString('base64');
      } else if (typeof chunkData === 'string') {
        // 对于字符串类型，直接使用
        encodedData = chunkData;
      } else {
        // 对于其他类型，转换为 JSON 字符串
        encodedData = JSON.stringify(chunkData);
      }

      await axios.post(
        `${baseUrl}/solve-request/${uuid}/${requestId}`,
        {
          bufferData: encodedData,
          chunkIndex: chunkIndex,
          isComplete: chunkIndex === totalChunks - 1,
          flag: '0001',
        },
        {
          timeout: 60000,
        },
      );
      console.log('发送单个数据块到代理服务器: ', chunkIndex);
    } catch (error) {
      console.error(
        `Error sending chunk ${chunkIndex} for request ${requestId}:`,
        error,
      );
    }
  }

  async transferRequest(
    uuid: string,
    requestId: string,
    config: AxiosRequestConfig,
  ) {
    if (!this.readyData.has(`${uuid}__${requestId}`)) {
      this.readyData.set(`${uuid}__${requestId}`, {
        chunkSize: undefined,
        currentIndex: 0,
        completeCount: 0,
        data: new Map<number, string>(), // 明确指定类型为 Map<number, string>
      });
    }
    let res;
    try {
      delete config.headers['host'];

      // 设置响应类型为stream以支持大文件处理
      res = await axios({
        ...config,
        responseType: 'stream',
        httpsAgent: new https.Agent({
          rejectUnauthorized: false,
        }),
        timeout: 300000, // 5分钟超时
      });

      console.log('res 0020: ', res);
      // 处理响应数据
      const processedData = await this.processResponseInChunks(
        uuid,
        requestId,
        res,
      );
      console.log('res 0007: ', processedData);
    } catch (error) {
      console.error('error 0008: ', error);
      res = { data: error };

      // 发送错误信息
      try {
        await axios.post(
          `${baseUrl}/solve-request/${uuid}/${requestId}`,
          {
            data: { error: error.message || 'Unknown error' },
            chunkInfo: { index: 0, total: 1, isComplete: true },
            flag: '0002',
          },
          {
            timeout: 60000,
          },
        );
      } catch (sendError) {
        console.error('error 0013: ', sendError);
      }
    }

    return res?.data;
  }

  private async sendChunkSize(uuid: string, requestId: string) {
    console.log(
      '发送数据块个数到服务器',
      this.readyData.get(`${uuid}__${requestId}`).chunkSize,
    );
    await axios.post(`${baseUrl}/solve-request/${uuid}/${requestId}`, {
      end: true,
      chunkSize: this.readyData.get(`${uuid}__${requestId}`).chunkSize,
      flag: '0003',
    });
  }

  // 读取配置文件
  private readConfig(): any {
    try {
      const configPath = path.join(process.cwd(), 'config.yaml');
      if (!fs.existsSync(configPath)) {
        console.warn('配置文件不存在:', configPath);
        return {};
      }

      const configContent = fs.readFileSync(configPath, 'utf8');
      const config = yaml.load(configContent) as any;
      console.log('成功读取配置文件:', config);
      return config;
    } catch (error) {
      console.error('读取配置文件失败:', error);
      return {};
    }
  }

  // 获取UUID
  private getUuid(): string | undefined {
    const config = this.readConfig();
    return config?.uuid;
  }
}
